// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WWEB_WIDGET_H_
#define WWEB_WIDGET_H_

#include <set>
#include <bitset>

#include <Wt/WString>
#include <Wt/WWidget>
#include <Wt/WEvent>

#ifdef WT_CNOR
#include <Wt/WJavaScript>
#endif // WT_CNOR

namespace Wt {

class WStringStream;
class WApplication;

/*! \brief Enumeration for a DOM element type.
 *
 * For internal use only.
 */
enum DomElementType {
  DomElement_A, DomElement_BR, DomElement_BUTTON, DomElement_COL,
  DomElement_COLGROUP,
  DomElement_DIV, DomElement_FIELDSET, DomElement_FORM,
  DomElement_H1, DomElement_H2, DomElement_H3, DomElement_H4,

  DomElement_H5, DomElement_H6, DomElement_IFRAME, DomElement_IMG,
  DomElement_INPUT, DomElement_LABEL, DomElement_LEGEND, DomElement_LI,
  DomElement_OL,

  DomElement_OPTION, DomElement_UL, DomElement_SCRIPT, DomElement_SELECT,
  DomElement_SPAN, DomElement_TABLE, DomElement_TBODY, DomElement_THEAD,
  DomElement_TFOOT, DomElement_TH, DomElement_TD, DomElement_TEXTAREA,
  DomElement_OPTGROUP,

  DomElement_TR, DomElement_P, DomElement_CANVAS,
  DomElement_MAP, DomElement_AREA, DomElement_STYLE,

  DomElement_OBJECT, DomElement_PARAM,
  
  DomElement_AUDIO, DomElement_VIDEO, DomElement_SOURCE,

  DomElement_B, DomElement_STRONG, DomElement_EM, DomElement_I, DomElement_HR,
  DomElement_UNKNOWN,
  DomElement_OTHER
};

class WCssDecorationStyle;
class WContainerWidget;
class DomElement;

#ifndef WT_CNOR
template <typename A1, typename A2, typename A3, typename A4,
	  typename A5, typename A6> class JSignal;
#endif

/*! \class WWebWidget Wt/WWebWidget Wt/WWebWidget
 *  \brief A base class for widgets with an HTML counterpart.
 *
 * All descendants of %WWebWidget implement a widget which corresponds
 * almost one-on-one with an HTML element. These widgets provide most
 * capabilities of these HTML elements, but rarely make no attempt to
 * do anything more.
 *
 * \sa WCompositeWidget
 */
class WT_API WWebWidget : public WWidget
{
public:
  /*! \brief Construct a WebWidget with a given parent.
   *
   * \sa WWidget::WWidget
   */
  WWebWidget(WContainerWidget *parent = 0);
  virtual ~WWebWidget();

  using WWidget::removeChild;

  virtual void setPositionScheme(PositionScheme scheme);
  virtual PositionScheme positionScheme() const;
  virtual void setOffsets(const WLength& offset, WFlags<Side> sides = All);
  virtual WLength offset(Side s) const;
  virtual void resize(const WLength& width, const WLength& height);
  virtual WLength width() const;
  virtual WLength height() const;
  virtual void setMinimumSize(const WLength& width, const WLength& height);
  virtual WLength minimumWidth() const;
  virtual WLength minimumHeight() const;
  virtual void setMaximumSize(const WLength& width, const WLength& height);
  virtual WLength maximumWidth() const;
  virtual WLength maximumHeight() const;
  virtual void setLineHeight(const WLength& height);
  virtual WLength lineHeight() const;
  virtual void setFloatSide(Side s);
  virtual Side floatSide() const;
  virtual void setClearSides(WFlags<Side> sides);
  virtual WFlags<Side> clearSides() const;
  virtual void setMargin(const WLength& margin, WFlags<Side> sides = All);
  virtual WLength margin(Side side) const;
  virtual void setHiddenKeepsGeometry(bool enabled);
  virtual bool hiddenKeepsGeometry() const;
  virtual void setHidden(bool hidden, const WAnimation& animation = WAnimation());
  virtual bool isHidden() const;
  virtual bool isVisible() const;
  virtual void setDisabled(bool disabled);
  virtual bool isDisabled() const;
  virtual bool isEnabled() const;
  virtual void setPopup(bool popup);
  virtual bool isPopup() const;
  virtual void setInline(bool isInline);
  virtual bool isInline() const;
  virtual void setDecorationStyle(const WCssDecorationStyle& style);
  virtual WCssDecorationStyle& decorationStyle();
  virtual const WCssDecorationStyle& decorationStyle() const;
  virtual void setStyleClass(const WT_USTRING& styleClass);
  void setStyleClass(const char *styleClass);
  virtual WT_USTRING styleClass() const;
  virtual void addStyleClass(const WT_USTRING& styleClass,
			     bool force = false);
  void addStyleClass(const char *styleClass, bool force = false);
  virtual void removeStyleClass(const WT_USTRING& styleClass,
				bool force = false);
  void removeStyleClass(const char *styleClass, bool force = false);
  virtual bool hasStyleClass(const WT_USTRING& styleClass) const;
  virtual void setVerticalAlignment(AlignmentFlag alignment,
				    const WLength& length = WLength());
  virtual AlignmentFlag verticalAlignment() const;
  virtual WLength verticalAlignmentLength() const;
  virtual void setToolTip(const WString& text,
			  TextFormat textFormat = PlainText);
  virtual void setDeferredToolTip(bool enable,
                                  TextFormat textFormat=PlainText);
  virtual WString toolTip() const;
  virtual void refresh();
  virtual void setAttributeValue(const std::string& name,
				 const WT_USTRING& value);
  virtual WT_USTRING attributeValue(const std::string& name) const;
  virtual void setJavaScriptMember(const std::string& name,
				   const std::string& value);
  virtual std::string javaScriptMember(const std::string& name) const;
  virtual void callJavaScriptMember(const std::string& name,
				    const std::string& args);
  virtual void load();
  virtual bool loaded() const;
  virtual int zIndex() const;

  virtual void setId(const std::string& id);
  virtual WWidget *find(const std::string& name);
  virtual WWidget *findById(const std::string& id);
  virtual void setSelectable(bool selectable);
  virtual void doJavaScript(const std::string& javascript);
  virtual const std::string id() const;

#ifdef WT_TARGET_JAVA
  /*! \brief Create DOM element for widget
   *
   * This is an internal function, and should not be called directly,
   * or be overridden!
   */
#endif
  virtual DomElement *createDomElement(WApplication *app);
#ifdef WT_TARGET_JAVA
  /*! \brief Get DOM changes for this widget
   *
   * This is an internal function, and should not be called directly,
   * or be overridden!
   */
#endif
  virtual void getDomChanges(std::vector<DomElement *>& result,
			     WApplication *app);
  virtual DomElementType domElementType() const = 0;

  DomElement *createStubElement(WApplication *app);
  DomElement *createActualElement(WWidget *self, WApplication *app);

  /*! \brief Change the way the widget is loaded when invisible.
   *
   * By default, invisible widgets are loaded only after visible content.
   * For tiny widgets this may lead to a performance loss, instead of the
   * expected increase, because they require many more DOM manipulations
   * to render, reducing the overall responsiveness of the application.
   *
   * Therefore, this is disabled for some widgets like WImage, or
   * empty WContainerWidgets.
   *
   * You may also want to disable deferred loading when JavaScript event
   * handling expects the widget to be loaded.
   *
   * Usually the default settings are fine, but you may want to change
   * the behaviour.
   *
   * \sa WApplication::setTwoPhaseRenderingThreshold()
   */
  void setLoadLaterWhenInvisible(bool);
  
  /*!
   * \brief returns the current html tag name
   * 
   * \sa setHtmlTagName()
   */
  std::string htmlTagName() const;

  /*!
   * \brief set the custom HTML tag name
   * 
   * The custom tag will replace the actual tag. 
   * The tag is not tested to see if
   * it is a valid one and a closing tag will always be added.
   *
   * \sa htmlTagName()
   */
  void setHtmlTagName(const std::string & tag);

  /*! \brief Escape HTML control characters in the text, to display literally (<b>deprecated</b>).
   *
   * \if cpp
   * \deprecated use Wt::Utils::htmlEncode() instead.
   * \elseif java
   * \deprecated use {@link Utils#htmlEncode(WString text, EnumSet flags)} instead.
   * \endif
   */
  static WString escapeText(const WString& text, bool newlinesToo = false);

  /*! \brief Escape HTML control characters in the text, to display literally (<b>deprecated</b>).
   *
   * \if cpp
   * \deprecated use Wt::Utils::htmlEncode() instead.
   * \elseif java
   * \deprecated use {@link Utils#htmlEncode(String text, EnumSet flags)} instead.
   * \endif
   */
  static std::string& escapeText(std::string& text, bool newlinestoo = false);

  /*! \brief Remove tags/attributes from text that are not passive (<b>deprecated</b>).
   *
   * This removes tags and attributes from XHTML-formatted text that
   * do not simply display something but may trigger scripting, and
   * could have been injected by a malicious user for Cross-Site
   * Scripting (XSS).
   *
   * This method is used by the library to sanitize XHTML-formatted
   * text set in WText, but it may also be useful outside the library
   * to sanitize user content when direcly using JavaScript.
   *
   * Modifies the \p text if needed. When the text is not proper
   * XML, returns \c false.
   *
   * \if cpp
   * \deprecated use Wt::Utils::removeScript() instead.
   * \elseif java
   * \deprecated use {@link Utils#removeScript(CharSequence text)} instead.
   * \endif
   */
  static bool removeScript(WString& text);

  /*! \brief Turn a UTF8 encoded string into a JavaScript string literal
   *
   * The \p delimiter may be a single or double quote.
   */
  static std::string jsStringLiteral(const std::string& v,
				     char delimiter = '\'');
  static std::string jsStringLiteral(const WString& v,
				     char delimiter = '\'');

  /*! \brief Returns contained widgets.
   *
   * \sa WContainerWidget::addWidget()
   */
  const std::vector<WWidget *>& children() const;

  /*! \brief %Signal emitted when children have been added or removed.
   *
   * \sa children()
   */
  Signal<>& childrenChanged();

  static std::string resolveRelativeUrl(const std::string& url);

  void setFormObject(bool how);
  static bool canOptimizeUpdates();
  void setZIndex(int zIndex);

  bool isRendered() const;

  virtual void setCanReceiveFocus(bool enabled);
  virtual bool canReceiveFocus() const;
  virtual bool setFirstFocus();
  virtual void setFocus(bool focus);
  virtual bool hasFocus() const;
  virtual void setTabIndex(int index);
  virtual int tabIndex() const;

  /*! \brief %Signal emitted when the widget lost focus.
   *
   * This signals is only emitted for a widget that canReceiveFocus()
   */
  EventSignal<>& blurred();

  /*! \brief %Signal emitted when the widget recieved focus.
   *
   * This signals is only emitted for a widget that canReceiveFocus()
   */
  EventSignal<>& focussed();

#ifndef WT_TARGET_JAVA
  using WWidget::setFocus;
#endif

  virtual bool scrollVisibilityEnabled() const WT_CXX11ONLY(final override);
  virtual void setScrollVisibilityEnabled(bool enabled) WT_CXX11ONLY(final override);
  virtual int scrollVisibilityMargin() const WT_CXX11ONLY(final override);
  virtual void setScrollVisibilityMargin(int margin) WT_CXX11ONLY(final override);
  virtual Signal<bool> &scrollVisibilityChanged() WT_CXX11ONLY(final override);
  virtual bool isScrollVisible() const WT_CXX11ONLY(final override);

  virtual void setThemeStyleEnabled(bool enabled);
  virtual bool isThemeStyleEnabled() const;

protected:
  typedef std::map<std::string, WObject *> FormObjectsMap;

  void repaint(WFlags<RepaintFlag> flags = 0);

  virtual void getFormObjects(FormObjectsMap& formObjects);
  virtual void doneRerender();
  virtual void updateDom(DomElement& element, bool all);
  virtual bool domCanBeSaved() const;
  virtual void propagateRenderOk(bool deep = true);
  virtual std::string renderRemoveJs(bool recursive);

  virtual void propagateSetEnabled(bool enabled);
  virtual void propagateSetVisible(bool visible);
  virtual bool isStubbed() const;
  virtual void enableAjax();

  virtual void addChild(WWidget *child);
  virtual void removeChild(WWidget *child);
  virtual void setHideWithOffsets(bool how = true);
  virtual WStatelessSlot *getStateless(Method method);

  WWidget *selfWidget();

  void doLoad(WWidget *w);
  void childAdded(WWidget *child);

  virtual void render(WFlags<RenderFlag> flags);

  virtual void signalConnectionsChanged();

private:
  /*
   * Booleans packed in a bitset.
   */
  static const int BIT_INLINE = 0;
  static const int BIT_HIDDEN = 1;
  static const int BIT_LOADED = 2;
  static const int BIT_RENDERED = 3;
  static const int BIT_STUBBED = 4;
  static const int BIT_FORM_OBJECT = 5;
  static const int BIT_IGNORE_CHILD_REMOVES = 6;
  static const int BIT_GEOMETRY_CHANGED = 7;
  static const int BIT_HIDE_WITH_OFFSETS = 8;
  static const int BIT_BEING_DELETED = 9;
  static const int BIT_DONOT_STUB = 10;
  static const int BIT_FLOAT_SIDE_CHANGED = 11;
  static const int BIT_REPAINT_TO_AJAX = 12;
  static const int BIT_HIDE_WITH_VISIBILITY = 13;
  static const int BIT_HIDDEN_CHANGED = 14;
  static const int BIT_ENABLED = 15; // caches isEnabled() for WInteractWidget
  static const int BIT_TOOLTIP_CHANGED = 16;
  static const int BIT_MARGINS_CHANGED = 17;
  static const int BIT_STYLECLASS_CHANGED = 18;
  static const int BIT_SET_UNSELECTABLE = 19;
  static const int BIT_SET_SELECTABLE = 20;
  static const int BIT_SELECTABLE_CHANGED = 21;
  static const int BIT_WIDTH_CHANGED = 22;
  static const int BIT_HEIGHT_CHANGED = 23;
  static const int BIT_DISABLED = 24;
  static const int BIT_DISABLED_CHANGED = 25;
  static const int BIT_CONTAINS_LAYOUT = 26;
  static const int BIT_ZINDEX_CHANGED = 27;
  static const int BIT_TOOLTIP_DEFERRED = 28;
  static const int BIT_GOT_FOCUS        = 29;
  static const int BIT_TABINDEX_CHANGED = 30;
  static const int BIT_SCROLL_VISIBILITY_ENABLED = 31;
  // BIT_SCROLL_VISIBILITY_LOADED makes sure that scrollVisibility.remove is never
  // called for widgets that never had scroll visibility enabled
  static const int BIT_SCROLL_VISIBILITY_LOADED = 32;
  static const int BIT_IS_SCROLL_VISIBLE = 33; // tracks whether the widget is currently "scroll visible"
  // Tracks whether scroll visibility is enabled/disabled, and whether the
  // scroll visibility margin has been modified.
  static const int BIT_SCROLL_VISIBILITY_CHANGED = 34;
  static const int BIT_THEME_STYLE_DISABLED = 35;

  static const char *FOCUS_SIGNAL;
  static const char *BLUR_SIGNAL;

  std::string elementTagName_;

#ifndef WT_TARGET_JAVA
  static const std::bitset<36> AllChangeFlags;
#endif // WT_TARGET_JAVA

  void loadToolTip();

  /*
   * Frequently used attributes.
   */
  std::bitset<36> flags_;
  WLength *width_;
  WLength *height_;

  /*
   * Data only stored transiently, during event handling.
   */
  struct TransientImpl {
    std::vector<std::string> childRemoveChanges_;
    std::vector<WWidget *>   addedChildren_;
    std::vector<WT_USTRING>  addedStyleClasses_, removedStyleClasses_;
    std::vector<std::string> attributesSet_;

    bool specialChildRemove_;
    WAnimation animation_;

    TransientImpl();
    ~TransientImpl();
  };

  TransientImpl *transientImpl_;

  struct LayoutImpl {
    PositionScheme	    positionScheme_;
    Side		    floatSide_;
    WFlags<Side>	    clearSides_;
    WLength		    offsets_[4]; // left, right, top, bottom
    WLength		    minimumWidth_;
    WLength		    minimumHeight_;
    WLength		    maximumWidth_;
    WLength		    maximumHeight_;
    int			    zIndex_; // -1 = wants popup
    AlignmentFlag	    verticalAlignment_;
    WLength		    verticalAlignmentLength_;
    WLength		    margin_[4];
    WLength                 lineHeight_;

    LayoutImpl();
  };

  LayoutImpl *layoutImpl_;

  struct LookImpl {
    WCssDecorationStyle    *decorationStyle_;
    WT_USTRING              styleClass_;
    WString                *toolTip_;
    TextFormat              toolTipTextFormat_;
    JSignal<>               loadToolTip_;

    LookImpl(WWebWidget *w);
    ~LookImpl();
  };

  mutable LookImpl *lookImpl_;

  struct DropMimeType {
    WT_USTRING hoverStyleClass;

    DropMimeType();
    DropMimeType(const WT_USTRING& hoverStyleClass);
  };

  enum JavaScriptStatementType { 
    SetMember, 
    CallMethod,
    Statement
  };

  struct OtherImpl {
    struct Member {
      std::string name;
      std::string value;
    };

    struct JavaScriptStatement {
      JavaScriptStatement(JavaScriptStatementType type,
			  const std::string& data);

      JavaScriptStatementType type;
      std::string data;
    };

    std::string                         *id_;

    std::map<std::string, WT_USTRING>   *attributes_;
    std::vector<Member>                 *jsMembers_;
    std::vector<JavaScriptStatement>    *jsStatements_;
    JSignal<int, int>                   *resized_;
    int                                  tabIndex_;

    // drag source id, drag mime type
    JSignal<std::string, std::string, WMouseEvent,
	    struct NoClass, struct NoClass, struct NoClass> *dropSignal_;
    JSignal<std::string, std::string, WTouchEvent,
	    struct NoClass, struct NoClass, struct NoClass> *dropSignal2_;

    typedef std::map<std::string, DropMimeType>   MimeTypesMap;
    MimeTypesMap                                 *acceptedDropMimeTypes_;

    Signal<> childrenChanged_;

    int scrollVisibilityMargin_;
    Signal<bool> scrollVisibilityChanged_;
    JSignal<bool> jsScrollVisibilityChanged_;

    OtherImpl(WWebWidget *const self);
    ~OtherImpl();
  };

  OtherImpl *otherImpl_;
  std::vector<WWidget *>    *children_;
  static std::vector<WWidget *> emptyWidgetList_;

  void renderOk();
  void calcZIndex();

  virtual bool needsToBeRendered() const;
  virtual void getSDomChanges(std::vector<DomElement *>& result,
			      WApplication *app);
  void         getSFormObjects(FormObjectsMap& formObjects);

  WWebWidget *parentWebWidget() const;
  void gotParent();

  /*
   * Drag & drop stuff.
   */
  bool          setAcceptDropsImpl(const std::string& mimeType,
				   bool accept,
				   const WT_USTRING& hoverStyleClass);

  void setIgnoreChildRemoves(bool how);
  bool ignoreChildRemoves() const;
  void beingDeleted();
  void setImplementLayoutSizeAware(bool aware);
  JSignal<int, int>& resized();

  void addJavaScriptStatement(JavaScriptStatementType type,
			      const std::string& data);
  int indexOfJavaScriptMember(const std::string& name) const;
  void declareJavaScriptMember(DomElement& element,
			       const std::string& name,
			       const std::string& value);
  WString storedToolTip() const;
  void undoSetFocus();

  void jsScrollVisibilityChanged(bool visible);

protected:
  void setRendered(bool rendered);

  void setId(DomElement *element, WApplication *app);
  virtual WWebWidget *webWidget() { return this; }

  EventSignal<> *voidEventSignal(const char *name, bool create);
  EventSignal<WKeyEvent> *keyEventSignal(const char *name, bool create);
  EventSignal<WMouseEvent> *mouseEventSignal(const char *name, bool create);
  EventSignal<WScrollEvent> *scrollEventSignal(const char *name, bool create);
  EventSignal<WTouchEvent> *touchEventSignal(const char *name, bool create);
  EventSignal<WGestureEvent> *gestureEventSignal(const char *name, bool create);

  void updateSignalConnection(DomElement& element, EventSignalBase& signal,
			      const char *name, bool all);

  virtual void parentResized(WWidget *parent, WFlags<Orientation> directions);
  void containsLayout();

  /*
   * WWebWidget ended up with more friends than me...
   */
  friend class WebRenderer;
  friend class WebSession;

  friend class WApplication;
  friend class WCompositeWidget;
  friend class WContainerWidget;
  friend class WCssDecorationStyle;
  friend class WCssTemplateRule;
  friend class WFont;
  friend class WGLWidget;
  friend class WInteractWidget;
  friend class JSlot;
  friend class WTable;
  friend class WViewWidget;
  friend class WWidget;
  friend class WTemplate;
};

}

#endif // WWEB_WIDGET_H_
